// Implementation of the Socket class.


#include "Socket.h"
#include "string.h"
#include <string.h>
#include <errno.h>
#include <fcntl.h>



Socket::Socket() : m_sock ( -1 )
{
  	memset ( &m_addr, 0, sizeof ( m_addr ) );
	//PrintErrors();
}

Socket::~Socket()
{
	fprintf(stdout, "Socket Kill Called\n");
  	if ( is_valid() )
    	::close ( m_sock );
}

bool Socket::close()
{
	return ::close( m_sock );
}

bool Socket::create()
{
  	m_sock = socket ( AF_INET, SOCK_STREAM, 0 );

  	if ( ! is_valid() )
    	return false;


  	// TIME_WAIT - argh
  	int on = 1;
  	if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 )
    	return false;

  	return true;
}

void Socket::PrintErrors()
{
	fprintf(stdout, "********** SOCKET ERRORS **********\n");
	fprintf(stdout, "EBADF = %d\n", EBADF);
	fprintf(stdout, "ENOTCONN = %d\n", ENOTCONN);
	fprintf(stdout, "ENOTSOCK = %d\n", ENOTSOCK);
	fprintf(stdout, "EAGAIN = %d\n", EAGAIN);
	fprintf(stdout, "EINTR = %d\n", EINTR);
	fprintf(stdout, "EFAULT = %d\n", EFAULT);
	fprintf(stdout, "EINVAL = %d\n\n", EINVAL);
}

bool Socket::hasMsg ( void )
{
	struct timeval tv = { 1, 0 };
	FD_ZERO(&infds);
	FD_SET(m_sock,&infds);
	return ::select (m_sock+1, &infds, NULL, NULL, &tv);
}

bool Socket::bind ( const int port )
{

  	if ( ! is_valid() )
    {
      	return false;
    }

  	m_addr.sin_family = AF_INET;
  	m_addr.sin_addr.s_addr = INADDR_ANY;
  	m_addr.sin_port = htons ( port );

  	int bind_return = ::bind ( m_sock, ( struct sockaddr * ) &m_addr, sizeof ( m_addr ) );

  	if ( bind_return == -1 )
    {
      	return false;
    }

  	return true;
}

bool Socket::listen() const
{
  	if ( ! is_valid() )
    {
      	return false;
    }

  	int listen_return = ::listen ( m_sock, MAXCONNECTIONS );

  	if ( listen_return == -1 )
    {
      	return false;
    }

  	return true;
}


bool Socket::accept ( Socket& new_socket ) const
{
  	int addr_length = sizeof ( m_addr );
  	new_socket.m_sock = ::accept ( m_sock, ( sockaddr * ) &m_addr, ( socklen_t * ) &addr_length );

  	if ( new_socket.m_sock <= 0 )
    	return false;
  	else
    	return true;
}


bool Socket::send ( const std::string s ) const
{
  	int status = ::send ( m_sock, s.c_str(), s.size(), MSG_NOSIGNAL );
  	if ( status == -1 )
    {
    	
      	return false;
    }
  	else
    {
      	return true;
    }
}

int Socket::recv ( std::string& s ) const
{
	char buf [ MAXRECV + 1 ];
	s = "";

  	memset ( buf, 0, MAXRECV + 1 );

  	int status = ::recv ( m_sock, buf, MAXRECV, 0);
  	if ( status == -1 )
    {
      	fprintf(stdout, "status == -1   errno == %d  in Socket::recv\n", errno);
      	return 0;
    }
  	else if ( status == 0 )
    {
      	return 0;
    }
  	else
    {
      	s = buf;    	
		return status;
    }
}

bool Socket::send ( void* p, unsigned int elems ) 
{
  	//fprintf(stdout, "Sending array of size %d..\n",elems);
  	int status = ::send ( m_sock, p, elems, MSG_MORE );
  	if ( status == -1 )
    {
    	fprintf(stdout, "status == -1   errno == %d  in Socket::send\n", errno);
      	return false;
    }
  	else
    {
      	return true;
    }
}

int Socket::recv ( void* p, unsigned int elems ) 
{
	//unsigned int* buf = new unsigned int [ MAXRECV + 1 ];
	//s = "";
	
  	//memset ( buf, 0, MAXRECV + 1 );
	//fprintf(stdout, "Recv array of size %d..\n",elems);
  	int status = ::recv ( m_sock, p, elems , MSG_WAITALL);
  	
  	if ( status == -1 )
    {
      	fprintf(stdout, "status == -1   errno == %d  in Socket::recv\n", errno);
      	return 0;
    }
  	else if ( status == 0 )
    {
      	return 0;
    }
  	else
    {
      	//p = buf;    	
		return status;
    }
}

bool Socket::connect ( const std::string host, const int port )
{
  	if ( ! is_valid() ) 
		return false;

  	m_addr.sin_family = AF_INET;
  	m_addr.sin_port = htons ( port );

  	int status = inet_pton ( AF_INET, host.c_str(), &m_addr.sin_addr );

  	if ( errno == EAFNOSUPPORT ) 
		return false;

  	status = ::connect ( m_sock, ( sockaddr * ) &m_addr, sizeof ( m_addr ) );

  	if ( status == 0 )
    	return true;
  	else
    	return false;
}

void Socket::set_non_blocking ( const bool b )
{

  	int opts;

  	opts = fcntl ( m_sock, F_GETFL );

  	if ( opts < 0 )
    {
      	return;
    }

  	if ( b )
    	opts = ( opts | O_NONBLOCK );
  	else
    	opts = ( opts & ~O_NONBLOCK );

  	fcntl ( m_sock, F_SETFL,opts );

}
